# 区分进程和线程
- 进程:是cpu资源分配的最小单位(是能够拥有资源和独立运行的最小单位,每个进程有一个或多个线程)
- 线程:是调度cpu资源的最小单位(是建立在进程基础之上的一次程序运行单位,常说的“单线程或多线程”都是指的在同一个进程中)
# 浏览器是多进程的
以下均以 Chrome 为例,在某些细节上,与 Firefox、Safari 不同,例如 Chrome 由渲染进程完成最终的绘制,而后两者则由主进程完成。
怎样理解:
- 浏览器是一个多进程应用
- 浏览器之所以能够运行,是因为系统为它分配了资源(cpu、内存)
- 没打开一个tab页,就会创建一个独立的进程
主要进程:
主进程(Browser进程),只有一个,负责协调主控:
- 负责头部区域的交互:前进、后退、刷新、收藏等;
- 负责tab页的管理:创建、销毁等;
- 网络资源的管理和下载。
插件进程,每个浏览器插件对应一个进程,仅在使用时才创建。
GPU 进程,最多一个,用于3D绘制等
渲染进程(浏览器内核),默认每个Tab页都是一个单独的进程,但当开多个空tab时,chrome会将其优化合并成一个线程
多进程优势:
- 利用沙盒模型隔离不同进程,当一个进程崩溃后,不会影响整个浏览器运行,提高了稳定性;
- 充分利用操作系统多核优势
多进程劣势:
- 资源(cpu、内存)消耗较大
# 渲染进程
主要线程:
- GUI 渲染线程
- 负责解析HTML、CSS,构建DOM树、CSSOM树,生成渲染树,布局、绘制等
- 重排、重绘
- 与 JS 线程互斥,JS 线程运行时,渲染线程将会挂起
- JS 引擎线程
- 即 JS 内核,如 V8 引擎,负责处理 JS 脚本程序
- 与渲染线程互斥,长时间执行会导致渲染卡顿,阻塞渲染
- 事件触发线程
- 控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
- 当JS引擎执行代码块如setTimeout、点击事件、ajax等,会将对应的回调事件注册到事件触发线程中
- 当对应的回调事件符合触发条件被触发时,该线程会把事件添加到事件队列的队尾,等待JS引擎的处理
- 当JS引擎空闲时会从事件队列中取出响应事件进行执行
- 定时器触发线程
- setInterval与setTimeout所在线程
- 浏览器定时计数并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
- 因此通过单独线程来计时并触发定时(开始计时时,将回调事件注册到事件触发线程中,计时结束后,由事件触发线程添加到事件队列中,等待JS引擎空闲后执行)
- W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms
- 异步http请求线程
- 负责处理异步请求
- 异步请求建立后,将对应回调注册到事件触发线程中,当异步状态变更时,由事件触发线程将对应回调事件(成功或失败)添加到事件队列中,等待JS引擎空闲后执行
# 进程间协作
有疑问:不同进程的职责
- 主进程创建渲染进程;
- 输入 url 后,主进程接管,并创建一个下载线程请求该 url 内容;
- 获取内容后通过 RendererHost 接口转交给渲染进程;
- 渲染进程渲染页面
# 渲染进程中线程间协作
# GUI 渲染线程与 JS 引擎线程互斥
- 由于 JS 可以操作 DOM ,做在渲染的同时修改 DOM 将造成渲染结果的不确定性,所以必须互斥。
- 同理,如果是多线程,每个线程都去修改 DOM,也会造成不确定性,所以必须是单线程。
- JS 引擎线程执行时间过程,将导致阻塞渲染或者交互卡顿。
- GUI 渲染线程会在 JS 引擎线程空闲时再执行。
# 解决阻塞渲染或交互卡顿问题
WebWorker
- 创建 Worker 时,JS 引擎线程向渲染进程申请开一个 Worker 线程,该线程不能操作 dom;
- JS 引擎线程与 Worker 线程间通过特定的方式通信(postMessage API,需要通过序列化对象来与线程交互特定的数据);
- 适用于处理耗时的 cpu 密集型大量计算任务,待任务执行结束后,将结果通知给 JS 引擎线程;
- JS 引擎的单线程本质没有改变。
区域与SharedWorker
- WebWorker 是渲染进程中的一个共享线程,而 SharedWorker 是所有同源页面(渲染线程)共享的一个进程;
- 可以实现同源 Tab 页面间的通信。
# 渲染流程
资源加载后触发相应事件:
- DOMContentLoaded:当 DOM 内容加载和解析完成时触发;
- onload:当所有 DOM、css、js、图片等都加载完成时触发;
- 注意是加载完成不是渲染完成
- 执行顺序:DOMContentLoaded 早于 onload
# css 文件加载的阻塞问题
- css 由异步http请求线程异步加载,不影响其它文件加载
- 不会阻塞 DOM 解析:异步加载时不影响 DOM 树解析和构建
- 会阻塞页面渲染:render 树需要 css 信息,所以会阻塞生成 render 树,从而阻塞页面渲染。如果 css 加载不影响生成 render 树,若未加载的 css 样式会影响某个 dom,当加载完成后,还要重新生成 render 树,甚至导致重排或重绘,影响性能。所以,css 加载会阻塞生成 render 树。
# 待解决疑问
页面的渲染(最终绘制)是由“主进程”或“GPU进程”还是“渲染进程的 GUI 线程”完成?
页面中遇到静态资源的加载(css、js),是由“主进程完成”还是“渲染进程的异步 HTTP 线程”完成?
异步请求应该是由“渲染进程的异步 HTTP 线程”完成的吧?
参考: https://segmentfault.com/a/1190000012925872